Skip to content

fix(cf): TTL=1 on proxied records + Global Key via EMAIL+KEY env vars#33

Merged
mastermanas805 merged 1 commit into
masterfrom
cf/staging-apply-fixes
May 30, 2026
Merged

fix(cf): TTL=1 on proxied records + Global Key via EMAIL+KEY env vars#33
mastermanas805 merged 1 commit into
masterfrom
cf/staging-apply-fixes

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

Follow-up to #32. First terraform-apply-staging.yml run failed with two distinct errors. This PR fixes both.

Fix 1 — DNS TTL on proxied records

CF rejects ttl=60 when proxied=true (CF manages TTL internally, must be 1). Updated all proxied DNS records to ttl=1; kept api at ttl=60 since it's grey-cloud today (becomes proxied in Phase 4 cut, will need flip then).

Fix 2 — Global Key auth via CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY

Global Keys (cfk_*) are NOT Bearer tokens. The CF TF provider needs them in X-Auth-Email + X-Auth-Key headers, only achieved by setting CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY env vars (not CLOUDFLARE_API_TOKEN). All 3 TF workflows + the operator-secrets guard updated.

The new secrets are already set on the infra repo via gh secret set (done out-of-band).

Test plan

  • CI: terraform.yml plan job runs cleanly with new EMAIL+API_KEY auth
  • CI: secrets-guard passes (5 secrets present, including the new two)
  • After merge: manual trigger of terraform-apply-staging.yml should now create all 19 staging resources without 9106 or "ttl must be 1" errors

Known follow-up

  • CLOUDFLARE_API_TOKEN secret on infra repo is now unused; can be deleted post-merge.
  • Long-term: rotate to a scoped cfat_* token (least-privilege) and revert to CLOUDFLARE_API_TOKEN env. Global Key works but is god-mode.

🤖 Generated with Claude Code

First apply attempt failed with two distinct errors:

  Error 1: ttl must be set to 1 when `proxied` is true
    on dns.tf line 16, in resource "cloudflare_dns_record" "apex"
  Error 2: 400 9106 "Authentication failed"
    on cache.tf line 16, in resource "cloudflare_ruleset" "api_cache_rules"

Fix 1 — DNS TTL
CF requires ttl=1 on every record where proxied=true (CF manages TTL
internally; 60 returns 400). The pre-cutover-ramp TTL=60 only applies
to grey-cloud records. Updated:
  - dns.tf: apex, www, staging[0] → ttl=1 (kept api at ttl=60, grey-cloud)
  - staging.tf: all 5 staging_* records → ttl=1 (all proxied)
  - dns.tf header comment updated with the proxied/ttl rule

Fix 2 — CF auth
The Global API Key (cfk_*) is NOT a Bearer-format token. The cloudflare
TF provider only uses Global Key auth (X-Auth-Email + X-Auth-Key
headers) when CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY env vars are set —
NOT when CLOUDFLARE_API_TOKEN is set (that forces Bearer). Bearer with
a Global Key works for SOME zone endpoints (DNS create) but fails on
Rulesets, R2, and other account-scoped endpoints with 9106.

All 3 TF workflows updated:
  - terraform.yml (plan)
  - terraform-apply-staging.yml
  - terraform-apply-production.yml

Each now passes CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY to TF instead of
CLOUDFLARE_API_TOKEN. Operator-secrets guard also updated to check the
new env vars (5 secrets total now: EMAIL + API_KEY + R2 AK + R2 SK +
CF_ACCOUNT_ID).

Secrets already set on infra repo via `gh secret set` before this PR
(CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY are populated).

terraform fmt + validate both clean locally.

After merge: re-trigger terraform-apply-staging.yml. Expected: all 19
staging resources create cleanly. The legacy CLOUDFLARE_API_TOKEN
secret is now unused — can be deleted in a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

Terraform plan — staging

✅ no changes

plan output
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudflare_account_token.admin_tunnel will be created
  + resource "cloudflare_account_token" "admin_tunnel" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + expires_on   = "2026-08-28T23:59:59Z"
      + id           = (known after apply)
      + issued_on    = (known after apply)
      + last_used_on = (known after apply)
      + modified_on  = (known after apply)
      + name         = "instanode-migration-admin-tunnel-staging"
      + policies     = [
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "ad7a6f88896d498f98eb30592abfbbf4"
                    },
                  + {
                      + id = "77efc2c0724d4c4eb94bfd9656247130"
                    },
                  + {
                      + id = "db37e5f1cb1a4e1aabaef8deaea43575"
                    },
                  + {
                      + id = "a1c0fec57cf94af79479a6d827fa518c"
                    },
                  + {
                      + id = "1e13c5124ca64b72b1969a67e8829049"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.613a9e74136364c781a8e258326019f9" = "*"
                    }
                )
            },
        ]
      + status       = (known after apply)
      + value        = (sensitive value)
    }

  # cloudflare_account_token.deploy will be created
  + resource "cloudflare_account_token" "deploy" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + expires_on   = "2026-11-26T23:59:59Z"
      + id           = (known after apply)
      + issued_on    = (known after apply)
      + last_used_on = (known after apply)
      + modified_on  = (known after apply)
      + name         = "instanode-migration-deploy-staging"
      + policies     = [
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "c4df38be41c247b3b4b7702e76eadae0"
                    },
                  + {
                      + id = "3030687196b94b638145a3953da2b699"
                    },
                  + {
                      + id = "c8fed203ed3043cba015a93ad1616f1f"
                    },
                  + {
                      + id = "c03055bc037c4ea9afb9a9f104b7b721"
                    },
                  + {
                      + id = "e17beae8b8cb423a99b1730f21238bed"
                    },
                  + {
                      + id = "ed07f6c337da4195b4e72a1fb2c6bcae"
                    },
                  + {
                      + id = "6d7f2f5f5b1d4a0e9081fdc98d432fd1"
                    },
                  + {
                      + id = "4755a26eedb94da69e1066d98aa820be"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.zone.08a1a569d2d6f9a713dc6d62103c5dc6" = "*"
                    }
                )
            },
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "dc44f27f48ab405392a5f69fe822bd01"
                    },
                  + {
                      + id = "8d28297797f24fb8a0c332fe0866ec89"
                    },
                  + {
                      + id = "bf7481a1826f439697cb59a20b22293e"
                    },
                  + {
                      + id = "f7f0eda5697f475c90846e879bab8666"
                    },
                  + {
                      + id = "e086da7e2179491d91ee5f35b3ca210a"
                    },
                  + {
                      + id = "d2a1802cc9a34e30852f8b33869b2f3c"
                    },
                  + {
                      + id = "c1fde68c7bcc44588cbb6ddbc16d6480"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.613a9e74136364c781a8e258326019f9" = "*"
                    }
                )
            },
        ]
      + status       = (known after apply)
      + value        = (sensitive value)
    }

  # cloudflare_dns_record.apex will be created
  + resource "cloudflare_dns_record" "apex" {
      + comment             = "marketing apex; CNAME-flattened to Pages project"
      + comment_modified_on = (known after apply)
      + content             = "instanode-web.pages.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.api will be created
  + resource "cloudflare_dns_record" "api" {
      + comment             = "api; grey-cloud today, orange-cloud per Phase 4 cut (D-3)"
      + comment_modified_on = (known after apply)
      + content             = "152.42.154.144"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "api.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = false
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 60
      + type                = "A"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging[0] will be created
  + resource "cloudflare_dns_record" "staging" {
      + comment             = "staging mirror per D-2"
      + comment_modified_on = (known after apply)
      + content             = "instant-staging.instanode.dev.cdn.cloudflare.net"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging_dashboard[0] will be created
  + resource "cloudflare_dns_record" "staging_dashboard" {
      + comment             = "staging dashboard — QA-only; D-5 keeps prod dashboard off Pages"
      + comment_modified_on = (known after apply)
      + content             = "instanode-dashboard-staging.pages.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "dashboard.staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging_deployment_anchor[0] will be created
  + resource "cloudflare_dns_record" "staging_deployment_anchor" {
      + comment             = "anchor for deployment wildcard CNAME (CF requires a real record at the parent)"
      + comment_modified_on = (known after apply)
      + content             = "100::"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "deployment.staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "AAAA"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging_deployment_wildcard[0] will be created
  + resource "cloudflare_dns_record" "staging_deployment_wildcard" {
      + comment             = "wildcard for /deploy/new staging apps (mirrors prod *.deployment.instanode.dev)"
      + comment_modified_on = (known after apply)
      + content             = "deployment.staging.instanode.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "*.deployment.staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging_webhook[0] will be created
  + resource "cloudflare_dns_record" "staging_webhook" {
      + comment             = "staging /webhook/new receiver subdomain"
      + comment_modified_on = (known after apply)
      + content             = "100::"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "webhook.staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "AAAA"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.staging_wildcard[0] will be created
  + resource "cloudflare_dns_record" "staging_wildcard" {
      + comment             = "wildcard for per-tenant CF Container services in staging; routed via cloudflare_workers_route below"
      + comment_modified_on = (known after apply)
      + content             = "staging.instanode.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "*.staging.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.www will be created
  + resource "cloudflare_dns_record" "www" {
      + comment             = "www → apex redirect handled by CF page rule"
      + comment_modified_on = (known after apply)
      + content             = "instanode.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "www.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_pages_domain.instanode_web will be created
  + resource "cloudflare_pages_domain" "instanode_web" {
      + account_id            = "613a9e74136364c781a8e258326019f9"
      + certificate_authority = (known after apply)
      + created_on            = (known after apply)
      + domain_id             = (known after apply)
      + id                    = (known after apply)
      + name                  = "staging.instanode.dev"
      + project_name          = "instanode-web-staging"
      + status                = (known after apply)
      + validation_data       = (known after apply)
      + verification_data     = (known after apply)
      + zone_tag              = (known after apply)
    }

  # cloudflare_pages_domain.staging_marketing[0] will be created
  + resource "cloudflare_pages_domain" "staging_marketing" {
      + account_id            = "613a9e74136364c781a8e258326019f9"
      + certificate_authority = (known after apply)
      + created_on            = (known after apply)
      + domain_id             = (known after apply)
      + id                    = (known after apply)
      + name                  = "staging.instanode.dev"
      + project_name          = "instanode-web-staging"
      + status                = (known after apply)
      + validation_data       = (known after apply)
      + verification_data     = (known after apply)
      + zone_tag              = (known after apply)
    }

  # cloudflare_pages_project.instanode_web will be created
  + resource "cloudflare_pages_project" "instanode_web" {
      + account_id             = "613a9e74136364c781a8e258326019f9"
      + build_config           = {
          + build_caching       = (known after apply)
          + build_command       = "npm run build"
          + destination_dir     = "dist"
          + root_dir            = ""
          + web_analytics_tag   = (known after apply)
          + web_analytics_token = (sensitive value)
        }
      + canonical_deployment   = (known after apply)
      + created_on             = (known after apply)
      + deployment_configs     = {
          + preview    = {
              + always_use_latest_compatibility_date = false
              + build_image_major_version            = 3
              + compatibility_date                   = "2026-05-30"
              + compatibility_flags                  = []
              + fail_open                            = true
              + usage_model                          = (known after apply)
            }
          + production = {
              + always_use_latest_compatibility_date = false
              + build_image_major_version            = 3
              + compatibility_date                   = "2026-05-30"
              + compatibility_flags                  = []
              + env_vars                             = {
                  + "VITE_API_URL" = {
                      + type  = "plain_text"
                      + value = (sensitive value)
                    },
                  + "VITE_ENV" = {
                      + type  = "plain_text"
                      + value = (sensitive value)
                    },
                }
              + fail_open                            = true
              + usage_model                          = (known after apply)
            }
        }
      + domains                = (known after apply)
      + framework              = (known after apply)
      + framework_version      = (known after apply)
      + id                     = (known after apply)
      + latest_deployment      = (known after apply)
      + name                   = "instanode-web-staging"
      + preview_script_name    = (known after apply)
      + production_branch      = "main"
      + production_script_name = (known after apply)
      + source                 = {
          + config = {
              + deployments_enabled            = (known after apply)
              + owner                          = "instanodedev"
              + owner_id                       = (known after apply)
              + path_excludes                  = (known after apply)
              + path_includes                  = (known after apply)
              + pr_comments_enabled            = true
              + preview_branch_excludes        = []
              + preview_branch_includes        = [
                  + "*",
                ]
              + preview_deployment_setting     = "all"
              + production_branch              = "main"
              + production_deployments_enabled = (known after apply)
              + repo_id                        = (known after apply)
              + repo_name                      = "instanode-web"
            }
          + type   = "github"
        }
      + subdomain              = (known after apply)
      + uses_functions         = (known after apply)
    }

  # cloudflare_r2_bucket.shared will be created
  + resource "cloudflare_r2_bucket" "shared" {
      + account_id    = "613a9e74136364c781a8e258326019f9"
      + creation_date = (known after apply)
      + id            = (known after apply)
      + jurisdiction  = "default"
      + location      = "WNAM"
      + name          = "instant-shared-staging"
      + storage_class = "Standard"
    }

  # cloudflare_r2_bucket_lifecycle.shared_anon_24h will be created
  + resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + bucket_name  = "instant-shared-staging"
      + jurisdiction = "default"
      + rules        = [
          + {
              + conditions                = {
                  + prefix = "anon/"
                }
              + delete_objects_transition = {
                  + condition = {
                      + max_age = 86400
                      + type    = "Age"
                    }
                }
              + enabled                   = true
              + id                        = "anon-24h"
            },
        ]
    }

  # cloudflare_ruleset.api_cache_rules will be created
  + resource "cloudflare_ruleset" "api_cache_rules" {
      + description  = "D-12 explicit-path allowlist for api.staging.instanode.dev"
      + id           = (known after apply)
      + kind         = "zone"
      + last_updated = (known after apply)
      + name         = "api-cache-rules"
      + phase        = "http_request_cache_settings"
      + rules        = [
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + cache = false
                }
              + description       = "bypass cache for all api.* paths by default"
              + enabled           = true
              + expression        = "(http.host eq \"api.staging.instanode.dev\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 0
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 30
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /healthz at edge for 30s — same SHA across instances"
              + enabled           = true
              + expression        = "(http.host eq \"api.staging.instanode.dev\") and (http.request.uri.path eq \"/healthz\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 60
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 300
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /openapi.json at edge for 5min"
              + enabled           = true
              + expression        = "(http.host eq \"api.staging.instanode.dev\") and (http.request.uri.path eq \"/openapi.json\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 600
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 3600
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /llms.txt at edge for 1h"
              + enabled           = true
              + expression        = "(http.host eq \"api.staging.instanode.dev\") and (http.request.uri.path eq \"/llms.txt\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
        ]
      + version      = (known after apply)
      + zone_id      = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_workers_route.staging_mongodb[0] will be created
  + resource "cloudflare_workers_route" "staging_mongodb" {
      + id      = (known after apply)
      + pattern = "mongo-*.staging.instanode.dev/*"
      + script  = "instanode-mongodb-staging"
      + zone_id = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_workers_route.staging_nats[0] will be created
  + resource "cloudflare_workers_route" "staging_nats" {
      + id      = (known after apply)
      + pattern = "nats-*.staging.instanode.dev/*"
      + script  = "instanode-nats-staging"
      + zone_id = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_workers_route.staging_pg_customers[0] will be created
  + resource "cloudflare_workers_route" "staging_pg_customers" {
      + id      = (known after apply)
      + pattern = "pg-customer-*.staging.instanode.dev/*"
      + script  = "instanode-pg-customers-staging"
      + zone_id = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_workers_route.staging_redis[0] will be created
  + resource "cloudflare_workers_route" "staging_redis" {
      + id      = (known after apply)
      + pattern = "redis-*.staging.instanode.dev/*"
      + script  = "instanode-redis-provision-staging"
      + zone_id = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

Plan: 21 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + admin_tunnel_token    = (sensitive value)
  + admin_tunnel_token_id = (known after apply)
  + deploy_token          = (sensitive value)
  + deploy_token_id       = (known after apply)

Warning: Resource Destruction Considerations

  with cloudflare_r2_bucket_lifecycle.shared_anon_24h,
  on r2.tf line 21, in resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h":
  21: resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h" {

This resource cannot be destroyed from Terraform. If you create this
resource, it will be present in the API until manually deleted.

Posted by terraform.yml run 26677339030. Apply requires manual trigger of terraform-apply-staging.yml.

@github-actions
Copy link
Copy Markdown

Terraform plan — production

✅ no changes

plan output
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudflare_account_token.admin_tunnel will be created
  + resource "cloudflare_account_token" "admin_tunnel" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + expires_on   = "2026-08-28T23:59:59Z"
      + id           = (known after apply)
      + issued_on    = (known after apply)
      + last_used_on = (known after apply)
      + modified_on  = (known after apply)
      + name         = "instanode-migration-admin-tunnel-production"
      + policies     = [
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "ad7a6f88896d498f98eb30592abfbbf4"
                    },
                  + {
                      + id = "77efc2c0724d4c4eb94bfd9656247130"
                    },
                  + {
                      + id = "db37e5f1cb1a4e1aabaef8deaea43575"
                    },
                  + {
                      + id = "a1c0fec57cf94af79479a6d827fa518c"
                    },
                  + {
                      + id = "1e13c5124ca64b72b1969a67e8829049"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.613a9e74136364c781a8e258326019f9" = "*"
                    }
                )
            },
        ]
      + status       = (known after apply)
      + value        = (sensitive value)
    }

  # cloudflare_account_token.deploy will be created
  + resource "cloudflare_account_token" "deploy" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + expires_on   = "2026-11-26T23:59:59Z"
      + id           = (known after apply)
      + issued_on    = (known after apply)
      + last_used_on = (known after apply)
      + modified_on  = (known after apply)
      + name         = "instanode-migration-deploy-production"
      + policies     = [
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "c4df38be41c247b3b4b7702e76eadae0"
                    },
                  + {
                      + id = "3030687196b94b638145a3953da2b699"
                    },
                  + {
                      + id = "c8fed203ed3043cba015a93ad1616f1f"
                    },
                  + {
                      + id = "c03055bc037c4ea9afb9a9f104b7b721"
                    },
                  + {
                      + id = "e17beae8b8cb423a99b1730f21238bed"
                    },
                  + {
                      + id = "ed07f6c337da4195b4e72a1fb2c6bcae"
                    },
                  + {
                      + id = "6d7f2f5f5b1d4a0e9081fdc98d432fd1"
                    },
                  + {
                      + id = "4755a26eedb94da69e1066d98aa820be"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.zone.08a1a569d2d6f9a713dc6d62103c5dc6" = "*"
                    }
                )
            },
          + {
              + effect            = "allow"
              + permission_groups = [
                  + {
                      + id = "dc44f27f48ab405392a5f69fe822bd01"
                    },
                  + {
                      + id = "8d28297797f24fb8a0c332fe0866ec89"
                    },
                  + {
                      + id = "bf7481a1826f439697cb59a20b22293e"
                    },
                  + {
                      + id = "f7f0eda5697f475c90846e879bab8666"
                    },
                  + {
                      + id = "e086da7e2179491d91ee5f35b3ca210a"
                    },
                  + {
                      + id = "d2a1802cc9a34e30852f8b33869b2f3c"
                    },
                  + {
                      + id = "c1fde68c7bcc44588cbb6ddbc16d6480"
                    },
                ]
              + resources         = jsonencode(
                    {
                      + "com.cloudflare.api.account.613a9e74136364c781a8e258326019f9" = "*"
                    }
                )
            },
        ]
      + status       = (known after apply)
      + value        = (sensitive value)
    }

  # cloudflare_dns_record.apex will be created
  + resource "cloudflare_dns_record" "apex" {
      + comment             = "marketing apex; CNAME-flattened to Pages project"
      + comment_modified_on = (known after apply)
      + content             = "instanode-web.pages.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.api will be created
  + resource "cloudflare_dns_record" "api" {
      + comment             = "api; grey-cloud today, orange-cloud per Phase 4 cut (D-3)"
      + comment_modified_on = (known after apply)
      + content             = "152.42.154.144"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "api.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = false
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 60
      + type                = "A"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_dns_record.www will be created
  + resource "cloudflare_dns_record" "www" {
      + comment             = "www → apex redirect handled by CF page rule"
      + comment_modified_on = (known after apply)
      + content             = "instanode.dev"
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + meta                = (known after apply)
      + modified_on         = (known after apply)
      + name                = "www.instanode.dev"
      + proxiable           = (known after apply)
      + proxied             = true
      + settings            = (known after apply)
      + tags                = (known after apply)
      + tags_modified_on    = (known after apply)
      + ttl                 = 1
      + type                = "CNAME"
      + zone_id             = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

  # cloudflare_pages_domain.instanode_web will be created
  + resource "cloudflare_pages_domain" "instanode_web" {
      + account_id            = "613a9e74136364c781a8e258326019f9"
      + certificate_authority = (known after apply)
      + created_on            = (known after apply)
      + domain_id             = (known after apply)
      + id                    = (known after apply)
      + name                  = "instanode.dev"
      + project_name          = "instanode-web"
      + status                = (known after apply)
      + validation_data       = (known after apply)
      + verification_data     = (known after apply)
      + zone_tag              = (known after apply)
    }

  # cloudflare_pages_project.instanode_web will be created
  + resource "cloudflare_pages_project" "instanode_web" {
      + account_id             = "613a9e74136364c781a8e258326019f9"
      + build_config           = {
          + build_caching       = (known after apply)
          + build_command       = "npm run build"
          + destination_dir     = "dist"
          + root_dir            = ""
          + web_analytics_tag   = (known after apply)
          + web_analytics_token = (sensitive value)
        }
      + canonical_deployment   = (known after apply)
      + created_on             = (known after apply)
      + deployment_configs     = {
          + preview    = {
              + always_use_latest_compatibility_date = false
              + build_image_major_version            = 3
              + compatibility_date                   = "2026-05-30"
              + compatibility_flags                  = []
              + fail_open                            = true
              + usage_model                          = (known after apply)
            }
          + production = {
              + always_use_latest_compatibility_date = false
              + build_image_major_version            = 3
              + compatibility_date                   = "2026-05-30"
              + compatibility_flags                  = []
              + env_vars                             = {
                  + "VITE_API_URL" = {
                      + type  = "plain_text"
                      + value = (sensitive value)
                    },
                  + "VITE_ENV" = {
                      + type  = "plain_text"
                      + value = (sensitive value)
                    },
                }
              + fail_open                            = true
              + usage_model                          = (known after apply)
            }
        }
      + domains                = (known after apply)
      + framework              = (known after apply)
      + framework_version      = (known after apply)
      + id                     = (known after apply)
      + latest_deployment      = (known after apply)
      + name                   = "instanode-web"
      + preview_script_name    = (known after apply)
      + production_branch      = "main"
      + production_script_name = (known after apply)
      + source                 = {
          + config = {
              + deployments_enabled            = (known after apply)
              + owner                          = "instanodedev"
              + owner_id                       = (known after apply)
              + path_excludes                  = (known after apply)
              + path_includes                  = (known after apply)
              + pr_comments_enabled            = true
              + preview_branch_excludes        = []
              + preview_branch_includes        = [
                  + "*",
                ]
              + preview_deployment_setting     = "all"
              + production_branch              = "main"
              + production_deployments_enabled = (known after apply)
              + repo_id                        = (known after apply)
              + repo_name                      = "instanode-web"
            }
          + type   = "github"
        }
      + subdomain              = (known after apply)
      + uses_functions         = (known after apply)
    }

  # cloudflare_r2_bucket.shared will be created
  + resource "cloudflare_r2_bucket" "shared" {
      + account_id    = "613a9e74136364c781a8e258326019f9"
      + creation_date = (known after apply)
      + id            = (known after apply)
      + jurisdiction  = "default"
      + location      = "WNAM"
      + name          = "instant-shared"
      + storage_class = "Standard"
    }

  # cloudflare_r2_bucket_lifecycle.shared_anon_24h will be created
  + resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h" {
      + account_id   = "613a9e74136364c781a8e258326019f9"
      + bucket_name  = "instant-shared"
      + jurisdiction = "default"
      + rules        = [
          + {
              + conditions                = {
                  + prefix = "anon/"
                }
              + delete_objects_transition = {
                  + condition = {
                      + max_age = 86400
                      + type    = "Age"
                    }
                }
              + enabled                   = true
              + id                        = "anon-24h"
            },
        ]
    }

  # cloudflare_ruleset.api_cache_rules will be created
  + resource "cloudflare_ruleset" "api_cache_rules" {
      + description  = "D-12 explicit-path allowlist for api.instanode.dev"
      + id           = (known after apply)
      + kind         = "zone"
      + last_updated = (known after apply)
      + name         = "api-cache-rules"
      + phase        = "http_request_cache_settings"
      + rules        = [
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + cache = false
                }
              + description       = "bypass cache for all api.* paths by default"
              + enabled           = true
              + expression        = "(http.host eq \"api.instanode.dev\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 0
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 30
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /healthz at edge for 30s — same SHA across instances"
              + enabled           = true
              + expression        = "(http.host eq \"api.instanode.dev\") and (http.request.uri.path eq \"/healthz\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 60
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 300
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /openapi.json at edge for 5min"
              + enabled           = true
              + expression        = "(http.host eq \"api.instanode.dev\") and (http.request.uri.path eq \"/openapi.json\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
          + {
              + action            = "set_cache_settings"
              + action_parameters = {
                  + browser_ttl = {
                      + default = 600
                      + mode    = "override_origin"
                    }
                  + cache       = true
                  + edge_ttl    = {
                      + default = 3600
                      + mode    = "override_origin"
                    }
                }
              + description       = "cache /llms.txt at edge for 1h"
              + enabled           = true
              + expression        = "(http.host eq \"api.instanode.dev\") and (http.request.uri.path eq \"/llms.txt\")"
              + id                = (known after apply)
              + logging           = (known after apply)
              + ref               = (known after apply)
            },
        ]
      + version      = (known after apply)
      + zone_id      = "08a1a569d2d6f9a713dc6d62103c5dc6"
    }

Plan: 10 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + account_id            = "613a9e74136364c781a8e258326019f9"
  + admin_tunnel_token    = (sensitive value)
  + admin_tunnel_token_id = (known after apply)
  + deploy_token          = (sensitive value)
  + deploy_token_id       = (known after apply)
  + zone_id               = "08a1a569d2d6f9a713dc6d62103c5dc6"

Warning: Resource Destruction Considerations

  with cloudflare_r2_bucket_lifecycle.shared_anon_24h,
  on r2.tf line 21, in resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h":
  21: resource "cloudflare_r2_bucket_lifecycle" "shared_anon_24h" {

This resource cannot be destroyed from Terraform. If you create this
resource, it will be present in the API until manually deleted.

Posted by terraform.yml run 26677339030. Apply requires manual trigger of terraform-apply-production.yml.

@mastermanas805 mastermanas805 merged commit 86922f2 into master May 30, 2026
6 checks passed
@mastermanas805 mastermanas805 deleted the cf/staging-apply-fixes branch May 30, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant